package joe.works.cacheimpl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import joe.works.cache.CacheEvent;
import joe.works.cache.ICache;
import joe.works.cache.ICahceListener;
import joe.works.commons.IDataLoader;

/**
 * A simple implementation of {@link ICache}
 * @author josseyj
 *
 * @param <Type>
 */
public class SimpleCache<Type> implements ICache<Type> {
	private List<Type> items;
	private IDataLoader<List<Type>> dataLoader;
	private CompositeCacheListener listeners = new CompositeCacheListener();
	private Date updatedDate;
	private long expireTimeInMinutes;
	private boolean autoReload;
	private Timer reloadTimer;
	private boolean loadOnStarup;
	
	public void setLoadOnStarup(boolean loadOnStarup) {
		this.loadOnStarup = loadOnStarup;
	}
	
	public void setAutoReload(boolean autoReload) {
		this.autoReload = autoReload;
	}
	
	public void setDataLoader(IDataLoader<List<Type>> dataLoader) {
		this.dataLoader = dataLoader;
	}
	
	public void setExpireTimeInMinutes(long expireTimeInMinutes) {
		this.expireTimeInMinutes = expireTimeInMinutes;
	}
	
	public void initialize() {
		if(loadOnStarup) {
			updateCache();
		}
	}

	public synchronized List<Type> getItems() {
		if(shouldLoad()) {
			updateCache();
		}
		return new ArrayList<Type>(items);
	}
	
	protected boolean shouldLoad() {
		return !isLoaded() || isExpired();
	}

	public synchronized boolean isExpired() {
		if(!isLoaded()) {
			return false;
		}
		if(expireTimeInMinutes <= 0) {
			return false;
		}
		long elapsedTime = updatedDate.getTime() - System.currentTimeMillis();
		long elapsedTimeInMinutes = elapsedTime/1000/60;
		if(elapsedTimeInMinutes > expireTimeInMinutes) {
			return true;
		}
		return false;
	}

	public synchronized boolean isLoaded() {
		return items != null;
	}
	
	private synchronized List<Type> loadItems() {
		List<Type> items = dataLoader.loadData();
		return items;
	}
	
	public void updateCache() {
		items = loadItems();
		cacheUpdated();
	}
	
	protected void cacheUpdated() {
		updatedDate = new Date();
		fireCacheUpdated();
		if(autoReload) {
			if(reloadTimer != null) {
				reloadTimer.cancel();
			}
			reloadTimer = new Timer();
			reloadTimer.schedule(new CacheReloader(), expireTimeInMinutes*60*1000);
		}
	}
	
	public void addCacheListener(ICahceListener listener) {
		listeners.addListener(listener);
	}

	public void removeCacheListener(ICahceListener listener) {
		listeners.removeListener(listener);
	}
	
	protected void fireCacheUpdated() {
		listeners.cacheUpdated(new CacheEvent<Type>(this));
	}
	
	private class CacheReloader extends TimerTask {

		public void run() {
			updateCache();
		}
		
	}
	
}
